package server.connector.http;

import server.logger.Logger;
import server.util.DateUtil;
import server.util.RequestUtil;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import java.util.Locale;
public class HttpProcessor implements Runnable {
    private static final byte[] ack =
            "HTTP/1.1 100 Continue\r\n\r\n".getBytes();

    //调用自己的连接器
    private HttpConnector connector = null;
    //处理器编号
    private int id = 0;
    //处理器线程名
    private String threadName;
    //服务器端口号
    private int serverPort = 0;
    //
    private HttpRequestImpl request = null;
    //
    private HttpResponseImpl response = null;
    //要处理的套接字
    private Socket socket = null;
    //连接器的代理服务器端口
    private int proxyPort = 0;
    private String proxyName = null;
    private Logger logger = null;
    private int status = Constants.PROCESSOR_IDLE;
    //协议版本是否是HTTP/1.1
    private boolean http11 = true;
    //客户端是否要求接收请求确认
    private boolean sendAck = false;
    //存放请求行
    private HttpRequestLine requestLine = new HttpRequestLine();

    //构造器
    public HttpProcessor(HttpConnector connector, int id) {
        this.connector = connector;
        this.id = id;
        this.serverPort = connector.getPort();
        this.threadName = "HttpProcessor[" + serverPort + "][" + id + "]";
        this.request = connector.createRequest();
        this.response = connector.createResponse();
        this.proxyName = connector.getProxyName();
        this.proxyPort = connector.getProxyPort();
        logger = Logger.getLogger(threadName);
    }

    private boolean started = false;
    //启动处理器
    public void start() throws Exception {
        if (started)
            throw new Exception("处理器" + threadName + "已经启动过了");
        started = true;
        //启动处理器线程
        threadStart();
    }

    private Thread thread = null;
    //启动处理器线程
    private void threadStart() {
        thread = new Thread(this, threadName);
        thread.setDaemon(true);
        thread.start();
    }
    //是否有可用Socket
    private boolean available = false;
    //给处理器套接字,并唤醒处理器
    public synchronized void putSocket(Socket socket) {
        this.socket = socket;
        available = true;
        notifyAll();
    }

    //实现Runnable接口-------------------------------------------------------------------
    private boolean stopped;
    //不懂
    private Object threadSync = new Object();
    @Override
    public void run() {
        logger.log("启动成功");
        Socket socket = null;
        while (!stopped) {
            //阻塞等待,直到有连接需要处理
             socket = getSocket();
            //等待下一个要处理的连接
            if (socket == null)
                continue;

            //处理连接
            try {
                process(socket);
            } catch (Throwable t) {
                logger.log("处理连接过程出错", t);
            }
            //处理完成，把处理器放到栈中等待下次使用
            connector.reuseProcessor(this);
        }
        //不懂
        synchronized (threadSync) {
            threadSync.notifyAll();
        }
    }
    //阻塞等待有可用的套接字
    private synchronized Socket getSocket() {
        while (!available) {
            try {
                wait();
            } catch (Exception e) {

            }
        }
        available = false;
        return socket;
    }
    //长连接
    private boolean keepAlive = false;
    private void process(Socket socket) {
        boolean ok = true;
//        boolean finishResponse = true;
        SocketInputStream input = null;
        OutputStream output = null;

        //获取封装后的输入流
        try {
            input = new SocketInputStream(socket.getInputStream(), connector.getBufferSize());
        } catch (Exception e) {
            logger.log("获取输入流失败", e);
            ok = false;
        }

        keepAlive = true;

        //开始不断读请求发送响应
        while (!stopped && ok && keepAlive) {
//            finishResponse = true;
            try {
                request.setStream(input);
                request.setResponse(response);
                output = socket.getOutputStream();
                response.setStream(output);
                response.setRequest(request);
                response.setHeader("Server", Constants.SERVER_INFO);
            } catch (Exception e) {
                logger.log("获取输出流失败", e);
                ok = false;
            }

            if (ok) {
                try {
                    parseConnection(socket);
                    parseRequestLine(input, output);
                    //HTTP/0.9没有请求头
                    if (!request.getProtocol().startsWith("HTTP/0")) {
                        parseHeaders(input);
                    }
                    if(http11) {
                        //需要发送HTTP/1.1 100 Continue\r\n\r\n
                        if (sendAck)
                            output.write(ack);
                        if (connector.isChunkingAllowed()) {
                            response.setAllowChunking(true);
                        }
                    }
                } catch (Exception e) {
                    ok = false;
                    try {
                        logger.log("请求错误", e);
                        response.sendError(HttpServletResponse.SC_BAD_REQUEST);
                    } catch (Exception ex) {

                    }
                }
            }
            try {
                //消息发送的时间
                response.setHeader("Date", DateUtil.getCurrentDate());
                if (ok) {
                    connector.getContainer().invoke(request, response);
                }
            } catch (Exception e) {
                logger.log("servlet执行异常", e);
                try {
                    response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
                } catch (Exception ex) {

                }
                ok = false;
            }

//            if (finishResponse) {
//
//            }
            try {
                response.finishResponse();
            } catch (Exception e) {
                logger.log("发送响应失败", e);
                ok = false;
            }
            try {
                request.finishRequest();
            } catch (Exception e) {
                logger.log("解析请求失败", e);
                ok = false;
            }
            try {
                if (output != null)
                    output.flush();
            } catch (IOException e) {
                ok = false;
            }
            if ("close".equals(response.getHeader("Connection"))) {
                keepAlive = false;
            }
            status = Constants.PROCESSOR_IDLE;
            request.reuse();
            response.reuse();
        }
        try {
            shutdownInput(input);
            socket.close();
        } catch (Exception e) {

        }
    }
    private void shutdownInput(InputStream input) {
        try {
            int available = input.available();
            // skip any unread (bogus) bytes
            if (available > 0) {
                input.skip(available);
            }
        } catch (Throwable e) {
            ;
        }
    }
    //
    private void parseConnection(Socket socket) {
        request.setInet(socket.getInetAddress());
        if (proxyPort != 0)
            request.setServerPort(proxyPort);
        else
            request.setServerPort(serverPort);
        request.setSocket(socket);
    }
    //解析请求行
    private void parseRequestLine(SocketInputStream input, OutputStream output) throws Exception {
        //读取请求行
        input.readRequestLine(requestLine);
        status = Constants.PROCESSOR_ACTIVE;

        //判断请求行格式是否正确
        if (requestLine.methodLength < 1) {
            throw new ServletException("缺少请求方法");
        }
        if (requestLine.uriLength < 1) {
            throw new ServletException("缺少请求URI");
        }

        String method = new String(requestLine.method, 0, requestLine.methodLength);
        String uri = new String(requestLine.uri, 0, requestLine.uriLength);
        String protocol = new String(requestLine.protocol, 0, requestLine.protocolLength);
        //HTTP/0.9无协议版本
        if (protocol.length() == 0) {
            protocol = "HTTP/0.9";
        }
        if ("HTTP/1.1".equals(protocol)) {
            http11 = true;
            sendAck = false;
        } else {
            http11 = false;
            sendAck = false;
            keepAlive = false;
        }

        //解析查询字符串
        int questionIndex = uri.indexOf('?');
        if (questionIndex >= 0) {
            request.setQueryString(uri.substring(questionIndex + 1));
            uri = uri.substring(0, questionIndex);
        }
        //检查URI是否是绝对地址,是绝对地址要转换成相对地址
        if (!uri.startsWith("/")) {
            int index = uri.indexOf("://");
            if (index != -1) {
                index = uri.indexOf("/", index + 3);
                if (index == 0) {
                    uri = "";
                } else {
                    uri = uri.substring(index);
                }
            }
        }
        //检查URI中是否包含sessionid
        //例:http://localhost:8085/front;jseesionid=A7QWE89123KSNNASDAEQ11234
        int sessionIdBeginIndex = uri.indexOf(";jsessionid=");
        if (sessionIdBeginIndex >= 0) {
            //找到sessionid
            int uriIndex = sessionIdBeginIndex;
            sessionIdBeginIndex += ";jsessionid=".length();
            int sessionIdEndIndex = uri.indexOf(";", sessionIdBeginIndex);
            String sessionId = null;
            if (sessionIdEndIndex >= 0) {
                sessionId = uri.substring(sessionIdBeginIndex, sessionIdEndIndex);
                uri = uri.substring(0, uriIndex) + uri.substring(sessionIdEndIndex + 1);
            } else {
                sessionId = uri.substring(sessionIdBeginIndex);
                uri = uri.substring(0, uriIndex);
            }
            request.setRequestedSessionId(sessionId);
            request.setRequestedSessionIdFromURL(true);
        } else {
            request.setRequestedSessionId(null);
            request.setRequestedSessionIdFromURL(false);
        }
        //修正uri
        uri = RequestUtil.normalizeUri(uri);
        if (uri == null) {
            logger.log("URI错误: " + uri);
            throw new ServletException("URI错误: " + uri);
        }
        //设置方法，uri，协议
        request.setMethod(method);
        request.setRequestURI(uri);
        request.setProtocol(protocol);
        request.setSecure(connector.isSecure());
        request.setScheme(connector.getScheme());
    }

    /**
     * 从输入流中解析出请求头的内容放到request的headers中
     *
     * 对于像cookie、content-length...这样的请求头,会解析出内容做特殊处理
     */
    //transfer-encoding头未处理
    private void parseHeaders(SocketInputStream input) throws Exception {
        //一个header对象，省空间
        HttpHeader header = new HttpHeader();
        while (true) {
            //读一个请求头
            input.readHeader(header);
            //未读到name
            if (header.nameLength == 0) {
                //请求头全部读完
                if (header.valueLength == 0) {
                    return;
                } else { //异常情况
                    throw new Exception("请求头格式异常");
                }

            }
            //处理读到的请求头
            String name = new String(header.name, 0, header.nameLength);
            String value = new String(header.value, 0, header.valueLength);
            //添加到request中
            request.addHeader(name, value);
            //处理特殊请求头
            if ("authorization".equals(name)) {
                //请求头Authorization的值,一般用于存放账号密码
                request.setAuthorization(value);
            } else if ("accept-language".equals(name)) {
                Locale[] locales = RequestUtil.parseAcceptLanguage(value);
                for (Locale locale : locales) {
                    request.addLocale(locale);
                }
            } else if ("cookie".equals(name)) {
                //处理cookie
                Cookie[] cookies = RequestUtil.parseCookieHeader(value);
                for (int i = 0; i < cookies.length; i++) {
                    //从cookie中找sessionID
                    if ("JSESSIONID".equalsIgnoreCase(cookies[i].getName())) {
                        if (!request.isRequestedSessionIdFromCookie()) {
                            //cookie的sessionId比URL的优先级高
                            request.setRequestedSessionId(cookies[i].getValue());
                            request.setRequestedSessionIdFromCookie(true);
                            //有cookie直接无效URL
                            request.setRequestedSessionIdFromURL(false);
                        }
                    }
                    //添加cookie
                    request.addCookie(cookies[i]);
                }
            } else if ("content-length".equals(name)) {
                int length = -1;
                try {
                    length = Integer.parseInt(value);
                } catch (Exception e) {
                    throw new Exception("Content-Length字段值无效");
                }
                request.setContentLength(length);

            } else if ("content-type".equals(name)) {
                request.setContentType(value);
            } else if ("host".equals(name)) {
                int colonIndex = value.indexOf(':');
                if (colonIndex < 0) {
                    //host字段没有说明端口,使用默认端口
                    if ("http".equalsIgnoreCase(connector.getScheme())) {
                        request.setServerPort(80);
                    } else if ("https".equalsIgnoreCase(connector.getScheme())) {
                        request.setServerPort(443);
                    }
                    if (proxyName != null) {
                        request.setServerName(proxyName);
                    } else {
                        request.setServerName(value);
                    }
                } else {
                    if (proxyName != null)
                        request.setServerName(proxyName);
                    else
                        request.setServerName(value.substring(0, colonIndex).trim());
                    if (proxyPort != 0)
                        request.setServerPort(proxyPort);
                    else {
                        int port = 80;
                        try {
                            port = Integer.parseInt(value.substring(colonIndex + 1).trim());
                        } catch (Exception e) {
                            throw new Exception("host字段上的端口号无效");
                        }
                        request.setServerPort(port);
                    }
                }
            } else if ("connection".equals(name)) {
                if ("close".equals(value)) {
                    keepAlive = false;
                    response.setHeader("Connection", "close");
                }
            } else if ("expect".equals(name)) {
                if ("100-continue".equals(value)) {
                    sendAck = true;
                } else {
                    throw new Exception("expect字段异常");
                }
            } else if ("transfer-encoding".equals(name)) {
                //后续处理
            }

        }
    }

}
